/**
 * 组合模式
 */
//  组合模式用于 整体与部分的结构，当整体与部分有相似的结构，在操作时可以被一致对待时，就可以使用组合模式
// 例如：
// 文件夹和子文件夹的关系：文件夹中可以存放文件，也可以新建文件夹，子文件夹也一样。
// 总公司子公司的关系：总公司可以设立部门，也可以设立分公司，子公司也一样。
// 树枝和分树枝的关系：树枝可以长出叶子，也可以长出树枝，分树枝也一样。

// 组合模式最主要的功能就是让用户可以一致对待整体和部分结构，将两者都作为一个相同的组件，所以我们先新建一个抽象的组件类：
abstract class Component {
    // 职位
    private position: string;
    // 工作内容
    private job: string;

    constructor(position: string, job: string) {
        this.position = position;
        this.job = job;
    }
    // 做自己的本职工作
    work() {
        console.log('我是' + this.position + '，我正在' + this.job);
    }

    abstract addComponent(component: Component): void;

    abstract removeComponent(component: Component): void;

    abstract check(): void;
}

// 管理者继承自此抽象类：
class Manager extends Component {
    // 管理的组件
    private components: Set<Component> = new Set();

    constructor(position: string, job: string) {
        super(position, job);
    }

    addComponent(component: Component) {
        this.components.add(component);
    }

    removeComponent(component: Component) {
        this.components.delete(component);
    }

    // 检查下属
    check() {
        this.work();
        this.components.forEach(it => {
            it.check();
        });
    }
}
// 职员同样继承自此抽象类：
class Employee extends Component {
    constructor(position: string, job: string) {
        super(position, job);
    }
    addComponent(component: Component) {
        console.log('职员没有管理权限');
    }
    removeComponent(component: Component) {
        console.log('职员没有管理权限');
    }
    check() {
        this.work();
    }
}

const boss: Component = new Manager('老板', '唱怒放的生命');
const HR: Component = new Employee('人力资源', '聊微信');
const PM: Component = new Manager('产品经理', '不知道干啥');
const CFO: Component = new Manager('财务主管', '看剧');
const CTO: Component = new Manager('技术主管', '划水');
const UI: Component = new Employee('设计师', '画画');
const operator: Component = new Employee('运营人员', '兼职客服');
const webProgrammer: Component = new Employee('程序员', '学习设计模式');
const backgroundProgrammer: Component = new Employee('后台程序员', 'CRUD');
const accountant: Component = new Employee('会计', '背九九乘法表');
const clerk: Component = new Employee('文员', '给老板递麦克风');
boss.addComponent(HR);
boss.addComponent(PM);
boss.addComponent(CFO);
PM.addComponent(UI);
PM.addComponent(CTO);
PM.addComponent(operator);
CTO.addComponent(webProgrammer);
CTO.addComponent(backgroundProgrammer);
CFO.addComponent(accountant);
CFO.addComponent(clerk);

boss.check();

// 安全模式：Manager 类单独实现 addComponent 和 removeComponent 这两个方法

// 安全方式遵循了接口隔离原则，但由于不够透明，Manager 和 Employee 类不具有相同的接口，在客户端中，
// 我们无法将 Manager 和 Employee 统一声明为 Component 类了，必须要区别对待，带来了使用上的不方便。

// 安全方式和透明方式各有好处，在使用组合模式时，需要根据实际情况决定。但大多数使用组合模式的场景都是采用的透明方式，
// 虽然它有点不安全，但是客户端无需做任何判断来区分是叶子结点还是枝节点，用起来是真香。
